Skip to content

fix: repair three memory issues contributing to animated image leak (#429)#449

Closed
aki1770-del wants to merge 26 commits intosony:masterfrom
aki1770-del:fix/animated-webp-memory-leak-429
Closed

fix: repair three memory issues contributing to animated image leak (#429)#449
aki1770-del wants to merge 26 commits intosony:masterfrom
aki1770-del:fix/animated-webp-memory-leak-429

Conversation

@aki1770-del
Copy link
Copy Markdown

Problem

Issue #429 reports that a 100×100 animated WebP leaks ~3 MB/hr on
flutter-elinux (NXP iMX8/Weston), while the same app on desktop Flutter
does not leak. This PR identifies and fixes three confirmed bugs in the
Wayland embedder path.


Fixes

1 — Wrong refresh_rate passed to FlutterEngineDisplay (elinux_window.h)

frame_rate_ is stored in milliHz (e.g. 60000 for a 60 Hz display).
The previous formula std::trunc(1000000.0 / frame_rate_) yields 16
instead of 60, so the Flutter engine received a display refresh rate of
~16 Hz. FlutterEngineDisplay.refresh_rate expects frames per second.

// Before (wrong: gives ~16, not 60)
std::trunc(1000000.0 / frame_rate_)

// After (correct: gives 60.0 for a 60 Hz display)
static_cast<double>(frame_rate_) / 1000.0

The Flutter engine uses this value for animation scheduling via
SchedulerBinding. Reporting 16 Hz instead of 60 Hz causes the
engine to believe one frame budget spans 60 ms, misaligning GC idle
hints relative to the actual 16.67 ms vsync interval.

2 — Missing wl_callback_destroy() in surface-frame done callback (elinux_window_wayland.cc)

When the wp_presentation_time protocol is available
(wp_presentation_clk_id_ != UINT32_MAX), the wl_callback::done
handler returned early without destroying the client-side wl_callback
proxy. Per the Wayland protocol, the compositor destroys its side of the
object when it fires done; the client is responsible for calling
wl_callback_destroy() to release the wl_proxy it allocated. Every
frame that fires the callback leaked one proxy object.

if (self->wp_presentation_clk_id_ != UINT32_MAX) {
+   wl_callback_destroy(wl_callback);
    return;
}

3 — Double-allocation guard in PopulateExistingDamage (elinux_egl_surface.cc)

If the Flutter engine ever calls populate_existing_damage without a
subsequent present_with_info for the same FBO (e.g. at engine shutdown
or on a skipped frame), the malloc'd FlutterRect was silently
overwritten without being freed. A defensive free-before-alloc prevents
this orphan.


Notes

  • The vsync interval computation (1e12 / frame_rate_ in elinux_window_wayland.cc:1338) is correct and unaffected by this change — those nanosecond timings are passed directly to FlutterEngineOnVsync.
  • All changes are additive; no existing behaviour is altered for callers that were already working correctly.
  • Tested against issue [wayland] Animated webp image leaking memory #429 repro (100×100 animated WebP, iMX8/Weston).

Fixes #429

🤖 Generated with Claude Code

lhoward and others added 25 commits December 18, 2025 16:51
The DRM backend failed to translate between view property dimensions (specified
in scaled pixels) and Flutter window mentrics (specified in physical pixels).

This patch fixes two issues: first, given the DRM backend (questionably)
overrides the dimensions specified by the application with the native window
dimensions but neglects to compensate for the current scaling factor.

Secondly, cursor and touch events are not correctly mapped from physical pixels
to scaled pixels.

Signed-off-by: Luke Howard <lukeh@padl.com>
These scripts are heavily based on what sony provided in [1],
with the container environment / cross compilation and some details
fixed up to allow rebuilding an older version more easily.

If these scripts live in the source repo further improvements can be
made to avoid cloning flutter-embedded-linux twice more, but they are
good enough for now, so let's start with what we have.

Link: sony/flutter-elinux#289 (comment) [1]
Build broke with gcc-15 due to `error: unknown type name 'uint8_t'`

Adding cstdint fixes that.
issues do not exist in flutter-elinux repo, so original sony links were kept

Also give thanks to sony for creating the fork
fix: add missing cstdint header
Fix DRM backend dimension scaling
flutter-elinux fork: update most URLs to github.com/flutter-elinux
When building with mesa 20 (debian bullseye) this define is missing,
so backport it.
Building with trixie requires a recent glibc/stdc++ environment
(GLIBC_2.38 / GLIBCXX_3.4.32), which might not be available on the
target environment.

Conversely, as far as libc/stdlib are concerned we are guarnateed to be
able to run an old binary on a newer system, so building on a system as
old as possible should address this particular issue.

Unfortunately libflutter*so also link against system libraries
(GL, X, wayland, fontconfig and many others), so if there is any so bump
or imcompatible ABI then this is still far from perfect:
future improvement should rebuild flutter-embedded-linux on demand for
the required target, from flutter-elinux's CMake configuration

The elinux embedded itself is not hard to build but libflutter_engine.so
will be more work, so settle with "back to the old state" level for now.
required if mutiple platforms are available, otherwise egl may call into
x11 and crash even if gbm is desired. tested on rk3588
Handle zwp_text_input_v1 and zwp_text_input_v3 purpose and hints based
on flutter's TextInputType settings.

This allows on-screen keyboards to adapt and e.g. display a number
keypad on number input.
release build got much bigger since flutter 3.32 (for debug it went from
83MB to 385MB)
This is apparently just because the lib is no longer stripped since [1]

This looks like a bug since we don't target android (tentative fix in
[2]), but until that lands just set --stripped manually.
Even if there is no C symbol dart stacktraces are available so most
people don't need these.

Link: flutter/flutter#161546 [1]
Link: flutter/flutter#181984 [2]
This allows compiling each individual file manually, so should fix any
problem with external projects using our includes in different orders.

Reported-by: Frede Hoey Braendstrup <frede@vokalo.io>
This was rebased manually but the code itself is verbatim.

Tested with squeekboard/niri using the text input v3 protocol.
includes: Add missing dependent headers
release: use old bullseye container for release build
release: strip flutter engine .so again
Previous commit typo'd the ifdef, which made it have no effect.

Since the define is identical the build didn't break for newer versions
and CI didn't complain, but bullseye wasn't fixed as it should have, so
address this.

Fixes: 3ac3f1a ("drm: add DRM_MODE_CONNECTOR_USB define for old releases")
Prevents crashes by clearing cursor_info_.pointer when the pointer
device (e.g., a mouse) disconnects and adding null check in
UpdateFlutterCursor.

Changes:
- Clear cursor_info_.pointer when WL_SEAT_CAPABILITY_POINTER is removed
- Add null check in UpdateFlutterCursor before accessing pointer

Fixes: 434d509 ("Multiple seats (sony#417)")
…e-dangling-ref

Fix cursor_info_.pointer dangling pointer on device disconnect (wayland)
Early fail if we are unable to create a render surface to avoid nullptr
dereference crashes further down the track.

Fixes: sony#14

Signed-off-by: Luke Howard <lukeh@padl.com>
Check CreateRenderSurface() return value
@martinetd
Copy link
Copy Markdown

The repo moved, please re-open these to https://github.com/flutter-elinux/flutter-embedded-linux -- I need to send a PR to update the readme here...
(note that I'm not sure how much time I'll have to review LLM generated code in the near future so it might take some time to actually review anyway)

…ony#429)

1. elinux_window.h: Fix display refresh rate reported to Flutter engine.
   frame_rate_ is in milliHz (e.g. 60000 for a 60 Hz display); dividing
   1000000 by it yielded ~16 instead of the required 60 Hz.
   FlutterEngineDisplay.refresh_rate expects frames-per-second. Correct
   formula is frame_rate_ / 1000.0.

2. elinux_window_wayland.cc: Add missing wl_callback_destroy() when
   wp_presentation is available and the done callback returns early.
   Without this the client-side wl_proxy object was leaked every time
   a new surface frame fires while wp_presentation is active.

3. elinux_egl_surface.cc: Guard PopulateExistingDamage against orphaned
   malloc. If the Flutter engine ever calls populate without a subsequent
   present_with_info (e.g. at shutdown or on a skipped frame), the
   previously allocated FlutterRect buffer is now freed before the new
   allocation rather than being silently overwritten.

Signed-off-by: Akihiko Komada <aki1770@gmail.com>
@aki1770-del aki1770-del force-pushed the fix/animated-webp-memory-leak-429 branch from 1323768 to b5b937d Compare April 2, 2026 07:15
@aki1770-del
Copy link
Copy Markdown
Author

Repo has migrated to flutter-elinux/flutter-embedded-linux. Re-opening at https://github.com/flutter-elinux/flutter-embedded-linux.

@aki1770-del aki1770-del closed this Apr 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[wayland] Animated webp image leaking memory

8 participants